

#ifndef __CCE_MATERIAL_MATERIALMANAGER_H__
#define __CCE_MATERIAL_MATERIALMANAGER_H__

#include <qobject.h>
#include <qlist.h>
#include <qset.h>
#include <stdio.h>
#include <qcolor.h>
#include <qdebug.h>

#define CCE_MATERIAL_TEXTURES_FOLDER	"textures"

#define CCE_MATERIAL_SHADERNAME_MIME    "CrimsonCoreEditor/ShaderName"
#define CCE_MATERIAL_PROGRAMNAME_MIME   "CrimsonCoreEditor/ProgramName"
#define CCE_MATERIAL_EFFECTNAME_MIME    "CrimsonCoreEditor/EffectName"
#define CCE_MATERIAL_MATERIALNAME_MIME	"CrimsonCoreEditor/MaterialName"
#define CCE_MATERIAL_TEXTURENAME_MIME   "CrimsonCoreEditor/TextureName"

class cceMaterialItem : public QObject
{
  friend class cceMaterialManager;
  Q_OBJECT
public:

  enum ItemType
  {
    ItemType_Texture,
    ItemType_Shader,
    ItemType_Program,
    ItemType_Effect,
    ItemType_Material
  };

private: /* Attributes */
  ItemType  _type;

  bool      _new;

private: /* Methods */
  /**
   * Just emits the Removed signal
   */
  void Remove ();

protected:
  cceMaterialItem (ItemType type);

public:
  void SetNew (bool n) { _new = n; }
  bool IsNew () const { return _new; }

  ItemType GetItemType () const;


  /**
   * Just emits the Changed signal
   */
  void Change ();

signals:
  /**
   * @brief Emitted whenever the content of the material item has changed
   */
  void Changed ();

  /**
   * @brief Emitted when the material item was removed from the material manager
   */
  void Removed ();

};

class cceMaterialTexture : public cceMaterialItem
{
public:
  enum Wrap
  {
    Wrap_Repeat,
    Wrap_Clamp,
    Wrap_COUNT
  };

private:
  QString   _name;

  QString   _filename;

  QString   _group;

  Wrap      _wrap;


public:
  cceMaterialTexture ();

  void SetName (const QString& name) { _name = name; }
  const QString& GetName () const { return _name; }

  void SetFilename (const QString& filename) { _filename = filename; }
  const QString& GetFilename () const { return _filename; }

  void SetGroup (const QString& group) { _group = group; }
  const QString& GetGroup () const { return _group; }

  void SetWrap (Wrap wrap) { _wrap = wrap; }
  Wrap GetWrap () const { return _wrap; }

  static QString GetWrapName (Wrap wrap);
  static Wrap GetWrap(const QString& wrap);

};


enum cceMaterialPass
{
  Pass_Diffuse,
  Pass_Depth,
  Pass_Stencil,
  Pass_Shadow,
  Pass_COUNT
};



struct cceEffectTextureBindings
{
  /**
   * @brief The pass this requirement is for.
   */
  cceMaterialPass   Pass;

  /**
   * @brief The texture unit that is used in the given pass
   */
  int               Unit;

  /**
   * @brief The possible names, that where used within the given pass for the given texture unit
   */
  QSet<QString>     Bindings;

};


class cceMaterialShader : public cceMaterialItem
{
	Q_OBJECT
public:
	enum Type
	{
		Type_Vertex,
    Type_Fragment,
    Type_Geometry,
    Type_TesselationControl,
    Type_TesselationEval,
    Type_COUNT
  };
  enum BindingType
  {
    BindingType_Attribute,
    BindingType_Uniform,
    BindingType_In,
    BindingType_InOut,
    BindingType_COUNT
  };


private:
	Type			_type;
	QString		_name;
	QString		_code;

  struct Binding
  {
    BindingType   bindingType;
    QString       bindingTypeString;
    QString       typeName;
    QString       variable;
    QString       semantic;
    bool operator== (const Binding& other) const;
  };

  QList<Binding>  _bindings;

  void RegisterBinding (QList<Binding> &bindings, const QString& bindingType, const QString &typeName, const QString& name, const QString& semantic);

public:
	cceMaterialShader ();

  static cceMaterialItem::ItemType ItemType () { return ItemType_Shader; }

	void SetType (Type type) { _type = type; }
	Type GetType () const { return _type; }
  static QString GetTypeName (Type type);
	static Type GetType (const QString& typeName);
  static QString GetBindingName (BindingType bindingType);

	void SetName (const QString& name) { _name = name; }
	const QString& GetName () const { return _name; }

	void SetCode (const QString& code) { _code = code; }
	const QString& GetCode () const { return _code; }
  QString GetExportCode () const;

  QString GetVersionString () const;

  void UpdateBindings ();
  unsigned GetNumberOfBindings () const;
  const QString& GetBindingVariable (unsigned idx);
  const QString& GetBindingSemantic (unsigned idx);
  BindingType GetBindingType (unsigned idx);


signals:
  void BindingsChanged ();
};


class cceMaterialProgramTechnique : public QObject
{
  Q_OBJECT
private:
	QList<cceMaterialShader*>	_shaders;

private slots:
  void onShaderRemoved ();

public:
	cceMaterialProgramTechnique ();
	~cceMaterialProgramTechnique ();
	void AddShader (cceMaterialShader* shader);
	void RemoveShader (cceMaterialShader* shader);
	unsigned GetNumberOfShaders () const;
	cceMaterialShader* GetShader (unsigned idx) const;

signals:
  void Changed ();

};

	
class cceMaterialProgram : public cceMaterialItem
{
	Q_OBJECT
private:
	QString		_name;

	QList<cceMaterialProgramTechnique*>	_techniques;

public:
	cceMaterialProgram ();

  static cceMaterialItem::ItemType ItemType () { return ItemType_Program; }

  void SetName (const QString& name) { _name = name; }
	const QString& GetName () const { return _name; }

	void Clear ();
	void AddTechnique (cceMaterialProgramTechnique* technique);
	void RemoveTechnique (cceMaterialProgramTechnique* technique);
	unsigned GetNumberOfTechniques () const;
	cceMaterialProgramTechnique* GetTechnique (unsigned idx) const;


};

class cceMaterialEffectStage : public QObject
{
  Q_OBJECT
private:
  cceMaterialProgram    *_passes[Pass_COUNT];

private slots:
  void onProgramRemoved ();

public:
  cceMaterialEffectStage ();
  ~cceMaterialEffectStage ();

  void Clear ();

  void SetPass (cceMaterialPass pass, cceMaterialProgram* program);
  cceMaterialProgram* GetPass(cceMaterialPass pass);

  static QString GetPassName (cceMaterialPass pass);
	static cceMaterialPass GetPass (const QString& passName);

signals:
  void Changed ();
};



class cceMaterialEffect : public cceMaterialItem
{
  Q_OBJECT
private:
  QString _name;

  QList<cceMaterialEffectStage*>    _stages;
public:
  cceMaterialEffect ();
  ~cceMaterialEffect ();

  static cceMaterialItem::ItemType ItemType () { return ItemType_Effect; }

  void SetName (const QString& name) { _name = name; }
  const QString& GetName () const { return _name; }

  void Clear ();
  void AddStage (cceMaterialEffectStage* stage);
  void RemoveStage (cceMaterialEffectStage* stage);
  unsigned GetNumberOfStages () const;
  cceMaterialEffectStage* GetStage (unsigned idx) const;

  QList<cceEffectTextureBindings> GetTextureRequirements () const;
};


class cceMaterialTexturePack : public QObject
{
  Q_OBJECT
private:
  cceMaterialTexture    *_textures[16];

private slots:
  void onTextureRemoved ();

public:
  cceMaterialTexturePack ();

  void SetTexture (unsigned unit, cceMaterialTexture *texture);
  cceMaterialTexture *GetTexture (unsigned unit);

signals:
  void Changed ();
};

class cceMaterialMaterial : public cceMaterialItem
{
private:
  QString _name;
  QColor  _ambient;
  QColor  _diffuse;
  QColor  _specular;
  float   _shininess;
  QColor  _emission;
  bool    _testDepth;
  bool    _writeDepth;
  bool    _transparent;
  cceMaterialEffect *_effect;

  cceMaterialTexturePack  _textures[Pass_COUNT];

public:

  cceMaterialMaterial ();
  ~cceMaterialMaterial ();

  static cceMaterialItem::ItemType ItemType () { return ItemType_Material; }

  void SetName (const QString& name) { _name = name; }
  const QString& GetName () const { return _name; }

  void SetAmbient (const QColor &ambient) { _ambient = ambient; }
  const QColor& GetAmbient () const { return _ambient; }

  void SetDiffuse (const QColor &diffuse) { _diffuse = diffuse; }
  const QColor& GetDiffuse () const { return _diffuse; }

  void SetSpecular (const QColor &specular) { _specular = specular; }
  const QColor& GetSpecular () const { return _specular; }

  void SetShininess (float shininess) { _shininess = shininess; }
  float GetShininess () const { return _shininess; }

  void SetEmission (const QColor &emission) { _emission = emission; }
  const QColor& GetEmission () const { return _emission; }

  void SetTestDepth (bool testDepth) { _testDepth = testDepth; }
  bool IsTestDepth () const { return _testDepth; }

  void SetWriteDepth (bool writeDepth) { _writeDepth = writeDepth; }
  bool IsWriteDepth () const { return _writeDepth; }

  void SetTransparent (bool transparent) { _transparent = transparent; }
  bool IsTransparent () const { return _transparent; }

  void SetEffect (cceMaterialEffect* effect) { _effect = effect; }
  cceMaterialEffect* GetEffect () const { return _effect; }

  cceMaterialTexturePack* GetTextures (cceMaterialPass pass);
  void SetTexture (cceMaterialPass pass, unsigned unit, cceMaterialTexture* texture);
  cceMaterialTexture* GetTexture (cceMaterialPass pass, unsigned unit);

};


class cceMaterialManager : public QObject
{
	Q_OBJECT
private: /* Attributes */
  QList<cceMaterialTexture*>  _textures;
	QList<cceMaterialShader*>		_shaders;
	QList<cceMaterialProgram*>	_programs;
  QList<cceMaterialEffect*>   _effects;
  QList<cceMaterialMaterial*> _materials;


private slots:
  void onTextureChanged ();
  void onShaderChanged ();
  void onProgramChanged ();
  void onEffectChanged ();
  void onMaterialChanged ();

public:
	cceMaterialManager ();
  ~cceMaterialManager ();

public slots:
  void Clear ();
  void Export ();
  void SaveAll ();
  void SaveTextures ();
  void SaveShaders();
  void SavePrograms();
  void SaveEffects();
  void SaveMaterials();
  void UpdateGraphicsSystem();

public:

  void ClearTextures ();
  void AddTexture (cceMaterialTexture* texture);
  void RemoveTexture (cceMaterialTexture* texture);
  unsigned GetNumberOfTextures () const;
  cceMaterialTexture* GetTexture (unsigned idx) const;
  cceMaterialTexture* GetTexture (const QString& name) const;
	QString CopyTextureFilename (const QString& filename);
  cceMaterialTexture* GetTextureByFilename (const QString& filename, bool add = true);

  void ClearShaders ();
	void AddShader (cceMaterialShader* shader);
	void RemoveShader (cceMaterialShader* shader);
	unsigned GetNumberOfShaders () const;
	cceMaterialShader* GetShader (unsigned idx) const;
	cceMaterialShader* GetShader (const QString& name) const;

	
  void ClearPrograms ();
	void AddProgram (cceMaterialProgram *program);
	void RemoveProgram(cceMaterialProgram *program);
	unsigned GetNumberOfPrograms () const;
	cceMaterialProgram* GetProgram (unsigned idx) const;
	cceMaterialProgram* GetProgram (const QString& name) const;

  void ClearEffects ();
  void AddEffect (cceMaterialEffect *effect);
  void RemoveEffect (cceMaterialEffect *effect);
  unsigned GetNumberOfEffects () const;
  cceMaterialEffect* GetEffect (unsigned idx) const;
  cceMaterialEffect* GetEffect (const QString& name) const;

  void ClearMaterials ();
  void AddMaterial (cceMaterialMaterial *material);
  void RemoveMaterial (cceMaterialMaterial *material);
  unsigned GetNumberOfMaterials () const;
  cceMaterialMaterial* GetMaterial (unsigned idx) const;
  cceMaterialMaterial* GetMaterial (const QString& name) const;

signals:
  void TextureAdded (cceMaterialTexture *texture);
  void TextureChanged (cceMaterialTexture *texture);
  void TextureRemoved (cceMaterialTexture *texture);

  void ShaderAdded (cceMaterialShader* shader);
  void ShaderChanged (cceMaterialShader* shader);
  void ShaderRemoved (cceMaterialShader* shader);

  void ProgramAdded (cceMaterialProgram* program);
	void ProgramChanged (cceMaterialProgram* program);
	void ProgramRemoved (cceMaterialProgram* program);

  void EffectAdded (cceMaterialEffect* effect);
  void EffectChanged (cceMaterialEffect* effect);
  void EffectRemoved (cceMaterialEffect* effect);

  void MaterialAdded (cceMaterialMaterial* material);
  void MaterialChanged(cceMaterialMaterial* material);
  void MaterialRemoved (cceMaterialMaterial* material);
};

#endif /* ! __CCE_MATERIAL_MATERIALMANAGER_H__ */
